package com.xaicode.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.xaicode.auth.base.BaseMpServiceImpl;
import com.xaicode.auth.entity.SysDept;
import com.xaicode.auth.entity.SysUser;
import com.xaicode.auth.entity.SysUserPost;
import com.xaicode.auth.entity.SysUserRole;
import com.xaicode.auth.event.LoginCacheRefreshEvent;
import com.xaicode.auth.mapper.SysDeptMapper;
import com.xaicode.auth.mapper.SysUserMapper;
import com.xaicode.auth.security.AuthConst;
import com.xaicode.auth.security.PasswordEncoder;
import com.xaicode.auth.security.domain.dto.AssignRoleDto;
import com.xaicode.auth.security.domain.dto.SysUserDto;
import com.xaicode.auth.security.domain.params.SysRoleUserQueryParam;
import com.xaicode.auth.security.domain.params.SysUserQueryParam;
import com.xaicode.auth.security.domain.query.SysUserQueryCriteria;
import com.xaicode.auth.service.ISysUserService;
import com.xaicode.auth.utils.MpQueryHelper;
import com.xaicode.auth.utils.SpringContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageInfo;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class SysUserServiceImpl extends BaseMpServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;


    @Override
    public PageInfo<SysUser> queryPage(SysUserQueryParam query) {
        startPage(query);
        List<SysUser> list = queryList(query);
        return PageInfo.of(list);
    }

    @Override
    public List<SysUser> queryList(SysUserQueryParam query) {

        /*
        SELECT u.*
        FROM sys_user u
        left join sys_dept d on u.dept_id = d.id
        WHERE (u.is_deleted = '0' AND
            (
                u.dept_id = ${dept_id}
                OR u.dept_id in (select id from sys_dept where
                    parent_ids like '${dept_id},%' or parent_ids like '%,${dept_id},%' or parent_ids like '%,${dept_id}')
            ))
         */

        MPJLambdaWrapper<SysUser> mpjLW = new MPJLambdaWrapper<>();
        mpjLW.selectAll(SysUser.class);
        mpjLW.leftJoin(SysDept.class, SysDept::getId, SysUser::getDeptId);
        mpjLW.eq(StrUtil.isNotBlank(query.getMobile()), SysUser::getMobile, query.getMobile());
        mpjLW.like(StrUtil.isNotBlank(query.getNickname()), SysUser::getNickname, query.getNickname());
        mpjLW.like(StrUtil.isNotBlank(query.getUsername()), SysUser::getUsername, query.getUsername());

        // 起止时间
        List<LocalDateTime> lastActiveTimes = query.getLastActiveTime();
        if (Objects.nonNull(lastActiveTimes) && !lastActiveTimes.isEmpty()) {
            LocalDateTime startDateTimeL = lastActiveTimes.get(0);
            String startDateTime = startDateTimeL.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            mpjLW.ge(SysUser::getLastActiveTime, startDateTime);
            if (lastActiveTimes.size() > 1) {
                LocalDateTime endDateTimeL = lastActiveTimes.get(1);
                String endDateTime = endDateTimeL.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                mpjLW.ge(SysUser::getLastActiveTime, endDateTime);
            }
        }

        // 部门处理
        Integer deptId = query.getDeptId();
        if (Objects.nonNull(deptId)) {
            mpjLW.eq(SysUser::getDeptId, deptId);

            // 兼容子部门
            List<Integer> deptIds = sysDeptMapper.selectList(
                            Wrappers.<SysDept>lambdaQuery().select(SysDept::getId)
                                    .likeRight(SysDept::getParentIds, deptId + ",")
                                    .or()
                                    .like(SysDept::getParentIds, "," + deptId + ",")
                                    .or()
                                    .likeLeft(SysDept::getParentIds, "," + deptId))
                    .stream()
                    .map(SysDept::getId)
                    .collect(Collectors.toList());
            if (!deptIds.isEmpty()) {
                mpjLW.or(mpjLW2 -> {
                    mpjLW2.in(SysUser::getDeptId, deptIds);
                });
            }
        }

        List<SysUser> list = list(mpjLW);

        return list;
    }


    @Override
    public List<SysUser> list(SysUserQueryCriteria criteria) {
        QueryWrapper<SysUser> queryWrapper = MpQueryHelper.buildQueryWrapperWithDelFlag(criteria, SysUser.class, "u");
        // 根据部门查询
        if (criteria.getDeptId() != null) {
            queryWrapper.eq("u.dept_id", criteria.getDeptId())
                    .or()
                    .apply("u.dept_id in (select id from AUTH_SYS_DEPT where find_in_set({0}, parent_ids))", criteria.getDeptId());
        }
        return sysUserMapper.selectUserList(queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String createUser(SysUserDto dto) {
        String defaultPassword = AuthConst.DEFAULT_PASSWORD;
        // 密码加密
        dto.setPassword(PasswordEncoder.encode(defaultPassword));

        // 保存用户信息
        save(dto);

        // 保存用户岗位关系
        Optional.ofNullable(dto.getPostIds()).ifPresent(postIds -> {
            List<SysUserPost> posts = postIds.stream().map(postId -> {
                SysUserPost sysUserPost = new SysUserPost();
                sysUserPost.setUserId(dto.getId());
                sysUserPost.setPostId(postId);
                return sysUserPost;
            }).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(posts)) {
                sysUserMapper.insertBatchUserPost(posts);
            }
        });

        // 保存用户角色关系
        Optional.ofNullable(dto.getRoleIds()).ifPresent(roleIds -> {
            List<SysUserRole> roles = roleIds.stream().map(roleId -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setUserId(dto.getId());
                sysUserRole.setRoleId(roleId);
                return sysUserRole;
            }).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(roles)) {
                sysUserMapper.insertBatchUserRole(roles);
            }
        });

        return defaultPassword;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateUser(SysUserDto dto) {
        // 更新用户信息
        updateById(dto);

        // 删除并重新保存用户岗位关系
        sysUserMapper.deleteUserPostByUserId(dto.getId());
        Optional.ofNullable(dto.getPostIds()).ifPresent(postIds -> {
            List<SysUserPost> posts = postIds.stream().map(postId -> {
                SysUserPost sysUserPost = new SysUserPost();
                sysUserPost.setUserId(dto.getId());
                sysUserPost.setPostId(postId);
                return sysUserPost;
            }).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(posts)) {
                sysUserMapper.insertBatchUserPost(posts);
            }
        });

        // 删除并重新保存用户角色关系
        sysUserMapper.deleteUserRoleByUserId(dto.getId());
        Optional.ofNullable(dto.getRoleIds()).ifPresent(roleIds -> {
            List<SysUserRole> roles = roleIds.stream().map(roleId -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setUserId(dto.getId());
                sysUserRole.setRoleId(roleId);
                return sysUserRole;
            }).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(roles)) {
                sysUserMapper.insertBatchUserRole(roles);
            }
        });

        // 刷新缓存
        SpringContextHolder.getApplicationContext().publishEvent(new LoginCacheRefreshEvent(dto.getId()));
    }

    @Override
    public void assignRole(String userId, List<Integer> roleIdList) {
        // 删除并重新保存用户角色关系
        sysUserMapper.deleteUserRoleByUserId(userId);
        Optional.ofNullable(roleIdList).ifPresent(roleIds -> {
            List<SysUserRole> roles = roleIds.stream().map(roleId -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setUserId(userId);
                sysUserRole.setRoleId(roleId);
                return sysUserRole;
            }).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(roles)) {
                sysUserMapper.insertBatchUserRole(roles);
            }
        });
        // 更新缓存
        SpringContextHolder.getApplicationContext().publishEvent(new LoginCacheRefreshEvent(userId));
    }

    @Override
    public PageInfo<SysUser> roleUserList(SysRoleUserQueryParam param) {
        startPage(param);
        List<SysUser> list = sysUserMapper.selectRoleUserList(param);
        return PageInfo.of(list);
    }

    @Override
    public void cancelAuth(AssignRoleDto dto) {
        sysUserMapper.cancelAuth(dto);

        dto.getUserIds().forEach(userId -> {
            // 刷新缓存
            SpringContextHolder.getApplicationContext().publishEvent(new LoginCacheRefreshEvent(userId));
        });
    }

    @Override
    public void batchAssignRole(AssignRoleDto dto) {
        List<SysUserRole> roles = dto.getUserIds().stream().map(userId -> {
            SysUserRole sysUserRole = new SysUserRole();
            sysUserRole.setUserId(userId);
            sysUserRole.setRoleId(dto.getRoleId());
            return sysUserRole;
        }).collect(Collectors.toList());
        sysUserMapper.insertBatchUserRole(roles);

        dto.getUserIds().forEach(userId -> {
            // 刷新缓存
            SpringContextHolder.getApplicationContext().publishEvent(new LoginCacheRefreshEvent(userId));
        });
    }

    @Override
    public int countByRoleIds(List<Integer> roleIds) {
        return sysUserMapper.selectCountByRoleIds(roleIds);
    }

    @Override
    public long countByDeptIds(List<Integer> deptIds) {
        return count(new LambdaQueryWrapper<SysUser>()
                .in(SysUser::getDeptId, deptIds)
        );
    }

    @Override
    public int countByPostIds(List<Integer> postIds) {
        return sysUserMapper.selectCountByPostIds(postIds);
    }

}
